// 因为客户端不可能有多个用户同时访问，我们可以不考虑它的并发，链接消耗资源等问题，使用原始的socket编程
// 客户端也不需要写啥头文件，只需要联合时候服务器的头文件即可
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <ctime>
#include <thread>

#include <unistd.h>     // 包含 close() 函数（用于关闭套接字）
#include <sys/socket.h> // 核心套接字函数和数据结构，如 socket(), bind(), connect(), accept(), send(), recv()
#include <netinet/in.h> // 包含 IP 地址结构，如 struct sockaddr_in, IPPROTO_TCP, INADDR_ANY
#include <arpa/inet.h>  // 包含地址转换函数，如 inet_addr(), inet_pton(), inet_ntop()
#include <netdb.h>      // 用于主机名查询，如 gethostbyname(), getaddrinfo()

#include "User.hpp"
#include "Group.hpp"
#include "public.hpp"
#include "json.hpp"
#include "db.h"
using json = nlohmann::json;

// 用户信息
User g_user;
// 用户好友信息
std::vector<User> g_userfriendlist;
// 记录用户所在组的信息
std::vector<Group> g_usergrouplist;
// 我们肯定是需要两个线程的，因为如果只有一个线程，用户不输入就会阻塞在cin，还接受不了信息
void readTaskHanlder(int clinetfd);
// 获取当前时间
std::string getCurrentTime();
// 聊天主菜单(用户登录成功后就开启这个主菜单)
void ChatMenu();

void readTaskHanlder(int clinetfd)
{

}

// 显示当前登录成功的用户的信息
void ShowOnlineUserMessage()
{
    std::cout << "===========================================" << std::endl;

    std::cout << "================LOGIN  USER================" << std::endl;
    std::cout << "《current user》" << "id: " << g_user.getId() << " name: " << g_user.getName() << std::endl;

    std::cout << "================FRIEND LIST================" << std::endl;

    if (!g_userfriendlist.empty())
    {
        for (auto &e : g_userfriendlist)
        {
            std::cout << "friendid: " << e.getId() << "friendname: "
                      << e.getName() << "onlinestate: " << e.getState() << std::endl;
        }
    }

    std::cout << "================GROUP  LIST================" << std::endl;

    if (!g_usergrouplist.empty())
    {
        for (auto &e : g_usergrouplist)
        {
            std::cout << "groupid: " << e.getId() << "groupname: "
                      << e.getGroupname() << "groupdesc: " << e.getGroupdesc() << std::endl;
        }
    }

    std::cout << "===========================================" << std::endl;
}

std::string getCurrentTime()
{

}

void ChatMenu()
{

}

void Uages()
{
    std::cout << "Uages : ./Chatclient ip port" << std::endl;
}

// 巧记，命令行参数就是 （arg）c + （arg）v
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Uages();
        exit(0);
    }

    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    // 创建套接字
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        std::cerr << "errno create clientfd failed!" << std::endl;
        close(clientfd);
        exit(-1);
    }

    sockaddr_in saddr;
    memset(&saddr, 0, sizeof(sockaddr_in));

    // 设置saddr结构体准备链接，  要把port和ip主机信息转网络
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = inet_addr(ip.c_str());

    if (connect(clientfd, (const sockaddr *)&saddr, sizeof(sockaddr_in)) == -1)
    {
        std::cerr << "errno socket connect failed!" << std::endl;
        close(clientfd);
        exit(-1);
    }

    // 这就是客户端socket要干的事情，要创建套接字，要connect链接，而服务器就要设置监听了
    // （因为本项目用的是muduo网络库，所以服务器就不用管了）

    // 正式开始业务，首先应该是登录业务
    for (;;)
    {
        std::cout << "==============choice      service==============" << std::endl;
        std::cout << "==============choice 1 :    Login==============" << std::endl;
        std::cout << "==============choice 2 : register==============" << std::endl;
        std::cout << "==============choice 3 :     quit==============" << std::endl;

        int choice;
        std::cin >> choice;
        std::cin.get();
        // 这边一定要清楚输入缓冲区，不然后续会读到换行

        switch (choice)
        {
        case 1:
        {
            // 登录业务就显得相对比较复杂了
            int id;
            std::string password;
            std::cout << "Logining! please provide you" << std::endl;
            std::cout << "import id : " << std::endl;
            std::cin >> id;
            std::cin.ignore();
            std::cout << "import password : " << std::endl;
            std::cin >> password;
            std::cin.ignore();


            // 组装json对象
            json loginjs;
            loginjs["msgid"] = LOGIN_MSG;
            loginjs["id"] = id;
            loginjs["password"] = password;
            std::string loginstr = loginjs.dump();

            ssize_t len = send(clientfd, loginstr.c_str(), strlen(loginstr.c_str()) + 1, 0);
            if (len == -1)
            {
                std::cerr << "send Message of Login is failed" << std::endl;
            }
            {
                char buffer[1024];
                ssize_t n = recv(clientfd, buffer, sizeof(buffer) - 1, 0);
                if (n == -1)
                {
                    std::cerr << "recv Message of Login from Server is failed" << std::endl;
                }
                else
                {
                    // 现在接收到服务器返回的json字符串，我们还是得分类讨论
                    json responce = json::parse(buffer);
                    if (responce["error"].get<int>() == 1 || responce["error"].get<int>() == 2)
                    {
                        std::cout << responce["errorMeg"] << std::endl;
                    }
                    else
                    {
                        // 正式登陆成功，这里要解析的东西就比较多了
                        std::cout << "id : " << responce["id"].get<int>() << "name : " << responce["name"] << " " << responce["LoginMeg"] << std::endl;

                        // 设置全局变量
                        g_user.setId(responce["id"].get<int>());
                        g_user.setName(responce["name"]);

                        if (responce.find("FriendMsg") != responce.end())
                        {
                            std::vector<std::string> FriendMsg = responce["FriendMsg"];
                            // 由于FriendMsg里面还是json字符串，所以还要反序列化
                            for (auto &e : FriendMsg)
                            {
                                json friendjs = json::parse(e);

                                User friends;
                                friends.setId(friendjs["id"]);
                                friends.setName(friendjs["name"]);
                                friends.setState(friendjs["state"]);

                                g_userfriendlist.push_back(friends);
                            }
                        }

                        if (responce.find("GroupMeg") != responce.end())
                        {
                            std::vector<std::string> GroupMeg = responce["GroupMeg"];

                            for(auto & e : GroupMeg)
                            {
                                json groupjs = json::parse(e);

                                Group group;
                                group.setId(groupjs["groupid"]);
                                group.setGroupname(groupjs["groupname"]);
                                group.setGroupdesc(groupjs["groupdesc"]);

                                g_usergrouplist.push_back(group);
                            }
                        }
                        //至此，全局变量已经设置完成

                        ShowOnlineUserMessage();

                        // json : msgid, id , from(发起者的名字) , to , meg , time;
                        if (responce.find("OfflineMessage") != responce.end())
                        {
                            // 打印所有离线信息
                            std::vector<std::string> OfflineMeg = responce["OfflineMessage"];
                            for (auto &e : OfflineMeg)
                            {
                                std::cout << "from : " << responce["from"] << " He id : "
                                          << responce["id"].get<int>() << " include Message: "
                                          << responce["meg"] << " time : " << responce["time"] << std::endl;
                            }
                        }
                        //登录成功后需要，开启读线程专门负责读，不然整个客户端可能阻塞在cin输入
                        //设置分离线程，让操作系统管理该线程的释放(这就是CPP的reakTask类由C++11引入)
                        std::thread readTask(readTaskHanlder,clientfd);
                        readTask.detach();
                        //开启聊天菜单
                        ChatMenu();
                    }
                }
            }
        }
        break;
        case 2:
        {
            std::string name;
            std::string password;
            std::cout << "please set you" << std::endl;
            std::cout << "set name : " << std::endl;
            std::getline(std::cin, name);
            std::cout << "set password" << std::endl;
            std::getline(std::cin, password);

            json js;
            js["msgid"] = REG_MSG;
            js["name"] = name;
            js["password"] = password;
            std::string requset = js.dump();

            // string 转为 char*
            int len = send(clientfd, requset.c_str(), strlen(requset.c_str()) + 1, 0);
            if (len == -1)
            {
                // cout // cin // cerr
                std::cerr << "send message failed" << std::endl;
            }
            else
            {
                char buffer[1024];
                ssize_t n = recv(clientfd, buffer, sizeof(buffer) - 1, 0);
                if (n == -1)
                {
                    std::cerr << "recv no every message" << std::endl;
                }
                else
                {
                    // 至此，我们收到了一个被序列化发过来的字符串
                    json recvMeg = json::parse(buffer);

                    if (recvMeg["error"].get<int>() == 0)
                        std::cout << recvMeg["returnMessage"] << recvMeg["id"].get<int>() << std::endl;
                    else
                        std::cout << recvMeg["errorMessaege"] << std::endl;
                }
            }
        }
        break;
        case 3:
        {
            std::cout << "Bye Bye" << std::endl;
            exit(0);
        }
        break;
        default:
        {
            std::cerr << "choice errno please restart choice" << std::endl;
        }
        break;
        }
    }
    return 0;
}